package ru.osu.acm.testing.beans;

import ru.osu.acm.common.config.Config;
import ru.osu.acm.common.entities.Contest;
import ru.osu.acm.common.entities.Problem;
import ru.osu.acm.common.entities.Submission;
import ru.osu.acm.common.industrial.ContestFactory;
import ru.osu.acm.common.industrial.ProblemFactory;
import ru.osu.acm.common.industrial.SubmissionFactory;
import ru.osu.acm.common.industrial.TestcaseFactory;
import ru.osu.acm.testing.Utils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;
import javax.servlet.http.HttpSession;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;

/**
 * User: Aleksey
 * Date: 18.02.2009
 * Time: 13:36:40
 */
public class DataBean {
    @Resource(name = Config.jndi)
    private DataSource source;

    //  private PersonBean person = new PersonBean(-1, "guest", "", "", "guest", "guest", false, false);
    private UserBean user = new UserBean(new PersonBean(-1, "guest", "", "", "guest", "", false, false), null, false, false);
    private Integer selectedContest = null;
    private Integer selectedProblem;
    private String selectedLanguage;
    private String code;

    private ArrayList<SelectItem> contests = new ArrayList<SelectItem>();
    private ArrayList<Problem> problems = new ArrayList<Problem>();
    private ArrayList<SelectItem> selectProblems = new ArrayList<SelectItem>();
    private ArrayList<SelectItem> languages = new ArrayList<SelectItem>();

    //private Contest contest = new Contest(-1, "unknown", new Date(), new Date());

    private Integer lastLoadedContest = null;
    private int languageCount;

    @PostConstruct
    public void initialize() {
        StartupBean.initialize();
        languageCount = Config.compilers.size();
        for (String compilerName : Config.compilers.keySet()) {
            ru.osu.acm.common.config.Compiler compiler = Config.compilers.get(compilerName);
            languages.add(new SelectItem(compiler.getExtension(), compiler.getName() + " (" + compiler.getDescription() + ")"));
        }
        selectedLanguage = languages.get(0).getValue().toString();
    }

    public int getLanguageCount() {
        return languageCount;
    }

    public void setLanguageCount(int languageCount) {
        this.languageCount = languageCount;
    }

    /*public Contest getContest() {
        if (selectedContest == null) {
            return contest;
        }
        try {
            Connection conn = source.getConnection();
            try {
                PreparedStatement statement = conn.prepareStatement("select * from contest where id = ?");
                statement.setInt(1, selectedContest);
                ResultSet resultSet = statement.executeQuery();
                if (resultSet.next()) {
                    contest = new Contest(resultSet.getInt("id"),
                            resultSet.getString("contestname").trim(),
                            resultSet.getDate("startdate"),
                            resultSet.getDate("stopdate"));
                }
                resultSet.close();
                statement.close();
            }
            finally {
                conn.close();
            }
        }
        catch (SQLException e) {
            e.printStackTrace();
        }
        return contest;
    }*/

    public UserBean getUser() {
        return user;
    }

    public void setUser(UserBean user) {
        this.user = user;
    }


    public Integer getSelectedContest() {
        return selectedContest;
    }

    public void setSelectedContest(Integer selectedContest) {
        this.selectedContest = selectedContest;
    }

    public String getSelectedLanguage() {
        return selectedLanguage;
    }

    public void setSelectedLanguage(String selectedLanguage) {
        this.selectedLanguage = selectedLanguage;
    }

    public Integer getSelectedProblem() {
        return selectedProblem;
    }

    public void setSelectedProblem(Integer selectedProblem) {
        this.selectedProblem = selectedProblem;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public Integer getProblemCount() {
        getProblems();
        return problems.size();
    }

    public void setProblemCount() {
    }

    public ArrayList<Problem> getProblems() {
        if (selectedContest == null) {
            problems.clear();
            selectProblems.clear();
        }
        else {
            if (lastLoadedContest == null || !lastLoadedContest.equals(selectedContest)) {
                problems = loadProblems();
                lastLoadedContest = selectedContest;
                if (problems.size() > 0) {
                    selectedProblem = problems.get(0).getId();
                }
                selectProblems.clear();
                for (Problem problem : problems) {
                    selectProblems.add(new SelectItem(problem.getId(), problem.getIndex() + ". " + problem.getName()));
                }
            }
        }
        return problems;
    }

    public void setProblems(ArrayList<Problem> problems) {
        this.problems = problems;
    }

    public ArrayList<SelectItem> getSelectProblems() {
        getProblems();
        return selectProblems;
    }

    public void setSelectProblems(ArrayList<SelectItem> selectProblems) {
        this.selectProblems = selectProblems;
    }

    public Integer getContestCount() {
        getContests();
        return contests.size();
    }

    public void setContestCount() {
    }

    public ArrayList<SelectItem> getContests() {
        if (contests.size() == 0) {
            contests = loadContests();
            if (contests.size() > 0) {
                selectedContest = Integer.parseInt(contests.get(0).getValue().toString());
            }
        }
        return contests;
    }

    public void setContests(ArrayList<SelectItem> contests) {
        this.contests = contests;
    }

    public ArrayList<SelectItem> getLanguages() {
        return languages;
    }

    public void setLanguages(ArrayList<SelectItem> languages) {
        this.languages = languages;
    }

    // TODO: check if we can use ArrayList instead of array
    public ArrayList<Submission> getSubmissions() {
        return SubmissionFactory.getContestSubmissions(selectedContest, Utils.getConnection(source), true);
    }

    private ArrayList<Problem> loadProblems() {
        return ProblemFactory.getContestProblems(selectedContest, Utils.getConnection(source), true);
    }

    private ArrayList<SelectItem> loadContests() {
        ArrayList<SelectItem> items = new ArrayList<SelectItem>();
        ArrayList<Contest> contests = (user.getTeam() == null) ?
                ContestFactory.getUserContests(user.getPerson().getId(), Utils.getConnection(source), true) :
                ContestFactory.getTeamContests(user.getTeam().getId(), Utils.getConnection(source), true);
        for (Contest contest : contests) {
            items.add(new SelectItem(contest.getId(), contest.getName()));
        }
        return items;
    }


    public String getStarted() {
        try {
            Connection conn = source.getConnection();
            try {
                PreparedStatement statement = conn.prepareStatement("select startdate, stopdate from contest where contest.id = ?");
                statement.setInt(1, selectedContest);
                ResultSet resultSet = statement.executeQuery();
                if (resultSet.next()) {
                    Date currentDate = new Date();
                    java.sql.Timestamp startDate = resultSet.getTimestamp("startdate");
                    java.sql.Timestamp stopDate = resultSet.getTimestamp("stopdate");
                    if (currentDate.after(startDate) || currentDate.equals(startDate)) {
                        return "true";
                    }
                }
                statement.close();
                resultSet.close();
            }
            finally {
                conn.close();
            }
        }
        catch (SQLException e) {
            e.printStackTrace();
        }
        return "false";
    }

    public String getOver() {
        try {
            Connection conn = source.getConnection();
            try {
                PreparedStatement statement = conn.prepareStatement("select startdate, stopdate from contest where contest.id = ?");
                statement.setInt(1, selectedContest);
                ResultSet resultSet = statement.executeQuery();
                if (resultSet.next()) {
                    Date currentDate = new Date();
                    java.sql.Timestamp startDate = resultSet.getTimestamp("startdate");
                    java.sql.Timestamp stopDate = resultSet.getTimestamp("stopdate");
                    if (stopDate == null || (currentDate.before(stopDate) || currentDate.equals(stopDate))) {
                        return "true";
                    }
                }
                statement.close();
                resultSet.close();
            }
            finally {
                conn.close();
            }
        }
        catch (SQLException e) {
            e.printStackTrace();
        }
        return "false";
    }


    public String submit() {
        //
        boolean ok = false;
        try {
            Connection conn = source.getConnection();
            try {
                PreparedStatement statement = conn.prepareStatement("select startdate, stopdate from contest where contest.id = ?");
                statement.setInt(1, selectedContest);
                ResultSet resultSet = statement.executeQuery();
                if (resultSet.next()) {
                    Date currentDate = new Date();
                    java.sql.Timestamp startDate = resultSet.getTimestamp("startdate");
                    java.sql.Timestamp stopDate = resultSet.getTimestamp("stopdate");
                    if (stopDate == null || (currentDate.after(startDate) || currentDate.equals(startDate)) && (currentDate.before(stopDate) || currentDate.equals(stopDate))) {
                        ok = true;
                    }
                }
                statement.close();
                resultSet.close();
                if (!ok) {
                    return null;
                }
            }
            finally {
                conn.close();
            }
        }
        catch (SQLException e) {
            e.printStackTrace();
        }
        //

        Integer problemId = Integer.parseInt(Utils.getRequestParam("formsubmit:problem"));
        String compiler = Utils.getRequestParam("formsubmit:language");
        String code = Utils.getRequestParam("formsubmit:code");

        int newId = 1;
        try {
            Connection conn = source.getConnection();
            try {
                PreparedStatement statement = conn.prepareStatement("select submission.id from submission order by id desc limit 1");
                ResultSet resultSet = statement.executeQuery();
                if (resultSet.next()) {
                    newId = resultSet.getInt("id") + 1;
                }
                resultSet.close();
                statement.close();
            }
            finally {
                conn.close();
            }
        }
        catch (SQLException e) {
            e.printStackTrace();
        }

        int curPersonId = 0;
        int curTeamId = 0;
        if (user.getTeam() != null) {
            curTeamId = user.getTeam().getId();
        }
        else if (user.getPerson() != null) {
            curPersonId = user.getPerson().getId();
        }
        try {
            Connection conn = source.getConnection();
            try {
                PreparedStatement statement = conn.prepareStatement("insert into submission(id, person, team, problem, compiler, contest, source) values (?, ?, ?, ?, ?, ?, ?)");
                statement.setInt(1, newId);
                statement.setInt(2, curPersonId);
                statement.setInt(3, curTeamId);
                statement.setInt(4, problemId);
                statement.setString(5, compiler);
                statement.setInt(6, selectedContest);
                statement.setString(7, code);
                statement.executeUpdate();
                statement.close();
            }
            finally {
                conn.close();
            }
        }
        catch (SQLException e) {
            e.printStackTrace();
        }

        return "submissions";
    }

    public String login() {
        if (user.isLoggedIn()) {
            return null;
        }


        String username = Utils.getRequestParam("formlogin:username");
        String password = Utils.getRequestParam("formlogin:password");

        try {
            Connection conn = source.getConnection();
            try {
                PreparedStatement statement = conn.prepareStatement("select person.id, person.firstname, person.secondname, person.isadmin  from person, persongroup where login = ? and pass = ? ");
                statement.setString(1, username);
                statement.setString(2, password);
                ResultSet resultSet = statement.executeQuery();

                if (resultSet.next()) {
                    boolean haveadmin = (resultSet.getInt(4) == 1) ? true : false;
                    user = new UserBean(new PersonBean(resultSet.getInt(1),
                            username,
                            "",
                            "",
                            resultSet.getString(2).trim(),
                            resultSet.getString(3).trim(),
                            true,
                            haveadmin), null, true, haveadmin);
                    return "home";

                }
                resultSet.close();
                statement.close();
                statement = conn.prepareStatement("select team.id, team.teamname from team where login=?  and pass= ?");
                statement.setString(1, username);
                statement.setString(2, password);
                resultSet = statement.executeQuery();
                if (resultSet.next()) {
                    user = new UserBean(null, new TeamBean(resultSet.getInt("id"), username, "", resultSet.getString("teamname")), true, false);
                    return "home";
                }
                resultSet.close();
                statement.close();

            }
            finally {
                conn.close();
            }
        }
        catch (SQLException e) {
            e.printStackTrace();
        }


        return "login";
    }

    public String logout() {
        user = new UserBean(new PersonBean(-1, "guest", "", "", "guest", "", false), null, false, false);
        HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(false);
        session.invalidate();
        return "login";
    }

    public String submitSpecificProblem() {
        selectedProblem = Utils.getRequestParam("problem").equals("") ? selectedProblem : Integer.parseInt(Utils.getRequestParam("problem"));
        return "submit";
    }

    private String srcContestName;
    private String srcProblemName;
    private String srcUserName;
    private String srcLanguage;
    private String srcStatus;
    private String srcPlagStatus;
    private String srcSource;

    public String showSubmissionSource() {
        Integer submissionId = Integer.parseInt(Utils.getRequestParam("submission"));
        boolean isTeam = user.getTeam() != null ? true : false;
        //String nameSql = isTeam ? "team.teamname " : "CONCAT(person.firstname,' ',person.secondname) ";
        String nameSql = isTeam ? "team.teamname " : "person.secondname ";
        String idSQL = isTeam ? "team.id " : "person.id ";
        String whoSQL = isTeam ? "team" : "person";
        try {
            Connection conn = source.getConnection();
            try {
                PreparedStatement statement = conn.prepareStatement("select contest.contestname, " +
                        "problem.problemname, " +
                        nameSql + ", " +
                        "submission.compiler, " +
                        "submission.status, " +
                        "submission.plagStatus, " +
                        idSQL + ", " +
                        "submission.source" +
                        " from submission, problem, contest, " + whoSQL + " " +
                        " where submission.id = ? " +
                        "and submission.contest = contest.id " +
                        "and submission.problem = problem.id " +
                        "and submission." + whoSQL + " = " + whoSQL + ".id "
                );
                statement.setInt(1, submissionId);
                ResultSet resultSet = statement.executeQuery();
                if (resultSet.next()) {
                    srcContestName = resultSet.getString(1).trim();
                    srcProblemName = resultSet.getString(2).trim();
                    srcUserName = resultSet.getString(3).trim();
                    srcLanguage = resultSet.getString(4).trim();
                    srcStatus = Submission.statuses[resultSet.getInt(5)];
                    srcPlagStatus = Submission.plagiarismStatuses[resultSet.getInt(6)];
                    srcSource = resultSet.getString(8).trim();

                    int userId = resultSet.getInt(7);
                    int curUserId = -1;
                    if (isTeam) {
                        curUserId = user.getTeam().getId();
                    }
                    else {
                        curUserId = user.getPerson().getId();
                    }
                    if ((userId != curUserId) && !user.isIsadmin()) {
                        srcSource = "&lt;code hidden&gt;";
                    }
                    else {
                        srcSource = Utils.prepareSourceForHtml(srcSource);
                    }
                    return "source";
                }
                resultSet.close();
                statement.close();
            }
            finally {
                conn.close();
            }
        }
        catch (SQLException e) {
            e.printStackTrace();
        }

        return null;
    }

    private String srcTest;

    public String showSubmissionTest() {
        Integer problemId = Integer.parseInt(Utils.getRequestParam("problem"));
        Integer test = Integer.parseInt(Utils.getRequestParam("test"));
        String status = Utils.getRequestParam("status");

        if (test == 0 || status.equals("Received") || status.equals("Queued") || status.equals("Compiling") || status.equals("Running") || problemId == 26) { // TODO:
            return null;
        }
        test--;

        srcTest = TestcaseFactory.getInputTestcase(problemId, test, Utils.getConnection(source), true);
        srcTest = Utils.prepareSourceForHtml(srcTest);

        return "test";
    }

    public String getSrcContestName() {
        return srcContestName;
    }

    public void setSrcContestName(String srcContestName) {
        this.srcContestName = srcContestName;
    }

    public String getSrcProblemName() {
        return srcProblemName;
    }

    public void setSrcProblemName(String srcProblemName) {
        this.srcProblemName = srcProblemName;
    }

    public String getSrcUserName() {
        return srcUserName;
    }

    public void setSrcUserName(String srcUserName) {
        this.srcUserName = srcUserName;
    }

    public String getSrcLanguage() {
        return srcLanguage;
    }

    public void setSrcLanguage(String srcLanguage) {
        this.srcLanguage = srcLanguage;
    }

    public String getSrcStatus() {
        return srcStatus;
    }

    public void setSrcStatus(String srcStatus) {
        this.srcStatus = srcStatus;
    }

    public String getSrcPlagStatus() {
        return srcPlagStatus;
    }

    public void setSrcPlagStatus(String srcPlagStatus) {
        this.srcPlagStatus = srcPlagStatus;
    }

    public String getSrcSource() {
        return srcSource;
    }

    public void setSrcSource(String srcSource) {
        this.srcSource = srcSource;
    }

    public String getSrcTest() {
        return srcTest;
    }

    public void setSrcTest(String srcTest) {
        this.srcTest = srcTest;
    }

    public String nullEvent() {
        return null;
    }

    public static UserBean getUserBean() {
        FacesContext fc = FacesContext.getCurrentInstance();
        DataBean db = (DataBean) fc.getApplication().getVariableResolver().resolveVariable(fc, "data");
        return db.getUser();
    }

    public static int getContestId() {
        FacesContext fc = FacesContext.getCurrentInstance();
        DataBean db = (DataBean) fc.getApplication().getVariableResolver().resolveVariable(fc, "data");
        return db.getSelectedContest();
    }
}
